package v2;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.event.ComponentEvent;
import java.awt.event.ComponentListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.image.BufferedImage;
import java.io.File;
import java.util.ArrayList;
import java.util.List;

import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JPanel;

public class QDv2 extends JPanel implements MouseListener
, MouseMotionListener, ComponentListener, KeyListener,Runnable {

 JFrame frame;
 Thread thread;
 static double dist;
 static int kills=0,lives=0,level=0; 
 static GO player,near;
 static InvaderGOs invaders;
 static BulletGOs bullets;
 static Graphics gx;
 
 public QDv2(){frame=new JFrame(
		"Space Invaders, QuickAndDirtyImplementation by MohJB");
	frame.setContentPane(this);
	frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
	frame.setBounds(10,10, 800, 600);
	player=new GO();
	invaders=new InvaderGOs();invaders.init(10,20);
	bullets=new BulletGOs();bullets.init(15);
	addMouseListener(this);
	addMouseMotionListener(this);
	addKeyListener(this);
	addComponentListener(this);
	frame.setVisible(true);
	setDim(getSize());
	thread=new Thread(this);
	thread.start();}

 public void paint(Graphics g)
 {Dimension d=getDim();gx=g;
  g.clearRect(0,0, d.width ,d.height);
  g.drawString("kills:"+kills,10,10);
  g.drawString("lives:"+lives,100,10);
  g.drawString("level:"+level,200,10);
  g.drawString("invadersCount:"+invaders.count,10,30);
  g.drawString("nearest:"+dist,10,40);
	invaders.paint(g);
	bullets.paint(g);
	player.paint(g);
	if(near!=null)g.drawLine(player.x,player.y, near.x, near.y);
 }

 public void run()
 {Thread current=Thread.currentThread();
	while(thread==current)
	try{Dimension d=getDim();
		invaders.incr(d);
		bullets.incr(d);
		frame.repaint();
		thread.sleep(100);
	}catch(Exception ex){ex.printStackTrace();}}
////////////////////////////////////////////////////////////////
 /**GO=Graphical Object*/
 static class GO
 {int x,y,dx,dy;boolean on;

	boolean getOn(){return on;}
	void setOn(boolean p)
	{if(p!=on){if(on=p)fireOn();else fireOff();}}

	static Dimension goDim=new Dimension(16,16);
	void setDim(int w,int h){goDim.width=w;goDim.height=h;}
	Dimension dim(){return goDim;}

	static BufferedImage goImg;
	BufferedImage img(){return goImg;}
	void setImg(BufferedImage p){goImg=p;setDim(p.getWidth(),p.getHeight());}

	void loadImage(String fileName)
	{try{setImg(ImageIO.read(new File(fileName)));
	}catch(Exception ex){ex.printStackTrace();}}
	
	void paint(Graphics g){Dimension d=dim();
		int w=d.width,w2=w/2,h=d.height,h2=h/2;
		BufferedImage i=img();
		if(i==null)g.drawOval(x-w2, y-h2, w, h);
		else g.drawImage(i, x-w2, y-h2 ,null,null);}

	/**increment*/
	void incr(Dimension d)
	{x+=dx;y+=dy;Dimension dim=dim();
	 if(x<0||x+dim.width>=d.width
		||y<0||y+dim.height>=d.height)setOn(false);
	}

	boolean isCollide(GO o)/*
	{if(on&&o.on
		&&x<o.x+o.dim.width
		&&x+dim.width>o.x
		&&y<o.y+o.dim.height
		&&y+dim.height>o.y)return true;//test intersection
		return false;}
	{Dimension dim=dim(),d2=o.dim();return on&&o.on
		&&x<o.x+d2.width
		&&x+dim.width>o.x
		&&y<o.y+d2.height
		&&y+dim.height>o.y;}*/
	{Dimension dim=dim(),d2=o.dim();
	 int w1=dim.width/2,h1=dim.height/2
	 ,w2=d2.width/2,h2=d2.height/2;
	 return on&&o.on
		&&x-w1<o.x+w2
		&&x+w1>o.x-w2
		&&y-h1<o.y+h2
		&&y+h1>o.y-h1;}

	double dist(GO p){double a=x-p.x,b=y-p.y;return Math.sqrt(a*a+b*b);}

	GO isCollide(GOs a)
	{if(on)for(GO o:a.gos)if(isCollide(o))return o;
		return null;}

	boolean checkCollide(GO o)
	{if(isCollide(o))
	 {fireCollide(o);o.fireCollide(this);
		setOn(false);o.setOn(false);
		return true;}
	 return false;}

	List<GOListener>listeners;

	void addGOListener(GOListener gol)
	{if(listeners==null)listeners=new ArrayList<GOListener>();
	 if(!listeners.contains(gol))listeners.add(gol);}

	void removeGOListener(GOListener gol)
	{if(listeners==null)return;
	 listeners.remove(gol);}

	void fireOn(){if(listeners!=null)for(GOListener l:listeners)l.onOn(this);}
	void fireOff(){if(listeners!=null)for(GOListener l:listeners)l.onOff(this);}
	void fireCollide(GO p2){if(listeners!=null)for(GOListener l:listeners)l.onCollide(this,p2);}

 }//class GO=Graphical Object 
////////////////////////////////////////////////////////////////
 interface GOListener{void onOn(GO go);void onOff(GO go);void onCollide(GO go,GO g2);}
//////////////////////////////////////////////////////////////////
 static class Bullet extends GO{Bullet(){x=y=dx=0;dy=-15;}
 //void paint(Graphics g){g.drawOval(x-2, y-2, 4, 4);}
 }
////////////////////////////////////////////////////////////////
 static class Invader extends GO implements GOListener
 {Bullet bullet;long wait;
  Dimension idim=new Dimension(16,16);
  void setDim(int w,int h){idim.width=w;idim.height=h;}
  Dimension dim(){return idim;}

	BufferedImage iImg;
	BufferedImage img(){return iImg;}
	void setImg(BufferedImage p){iImg=p;setDim(p.getWidth(),p.getHeight());}

  Invader(){bullet=new Bullet();bullet.addGOListener(this);dx=3;bullet.dy=5;}

  void paint(Graphics g){if(bullet.on)bullet.paint(g);super.paint(g);}

  void init(int x,int y){this.x=x;this.y=y;wait=(long)Math.random()*300000;setOn(true);}

  void incr(Dimension d)
	{if(!on)return;Dimension dim=dim();
	 if((dx>0&&x+dx+dim.width>=d.width)||(dx<0&&x+dx<=0))
	 {y+=dim.height;dx=dx*-1;}
	 if(y>=d.height)y=1;
	 super.incr(d);
	 if(wait>0)wait--;
	 if(wait==0){wait--;shoot();}
	}
  void shoot(){bullet.x=x;bullet.y=y;bullet.setOn(true);}
  public void onOn(GO go){}
  public void onOff(GO go){wait=(long)Math.random()*300000;}
  public void onCollide(GO go,GO g2){wait=(long)Math.random()*100000;}
 }//class Invader
////////////////////////////////////////////////////////////////
 abstract class GOs
 {List<GO>gos;

	void paint(Graphics g)
	{for(GO o:gos)if(o.on)o.paint(g);}

	void incr(Dimension d){for(GO o:gos)if(o.on)o.incr(d);}

	abstract GO instance();

	GOs init(int n){gos=new ArrayList<GO>(n);
		for(int i=0;i<n;i++)gos.add(instance());return this;}

 }//class GOs
////////////////////////////////////////////////////////////////
 class BulletGOs extends GOs
 {GO instance(){return new Bullet();}

  void shootNew(){for(GO b:gos)if(!b.on){b.x=player.x;b.y=player.y;b.setOn(true);return;}}

  void incr(Dimension d)
  {for(GO bullet:gos)
	if(bullet.on)
	{bullet.incr(d);
	  for(GO invader:QDv2.invaders.gos)
		if(invader.on && bullet.checkCollide(invader))
			kills++;
	}
  }
 }//class BulletGOs
////////////////////////////////////////////////////////////////

 final String[]invaders_fileNames={"blue.png","elmo.png","ghost.png","green.png","mick43x59.png","red.png"};
 BufferedImage[]invaders_imgs;
 
 class InvaderGOs extends GOs
 {GO instance(){return new Invader();}
	int count,rows,cols;
	Bullet bullet;Dimension biggest=new Dimension(1,1);

  GOs init(int r,int c){init(r*c);rows=r;cols=c;init();return this;}

  void init()
  {Dimension d=gos.get(0).dim(),p=QDv2.getDim(),mx;
	count=rows*cols;level++;
	if(p==null)QDv2.setDim(p=getSize());
	if(invaders_imgs==null)for(int i=0;i>invaders_fileNames.length;i++)
	{try{invaders_imgs=new BufferedImage[invaders_fileNames.length];
	 invaders_imgs[i]=ImageIO.read(new File(//"/home/moh/Desktop/kgdug/monsters/"+
		invaders_fileNames[i]));}
	catch(Exception ex){ex.printStackTrace();}}
	for(GO g:gos){mx=g.dim();
		if(biggest.width <mx.width )biggest.width =mx.width;
		if(biggest.height<mx.height)biggest.height=mx.height;}
	mx=biggest;
	int n=gos.size()
	,ox=(p.width- mx.width *cols  )/cols
	,oy=(p.height-mx.height*rows*2)/rows
	,x,y,i=0,fi=0;
	for(int r=0;r<rows;r++)
	{	y=1+r*oy;
		for(int c=0;c<cols;c++)
		{Invader invader=(Invader)gos.get(i++);
			x=1+c*ox;
			invader.init(x, y);
			invader.setImg(invaders_imgs[fi]);
			fi=(fi+1)%invaders_imgs.length;
		}
	}
  }

  void incr(Dimension d)
  {count=0;near=null;
	double t=0;dist=d.width*2+d.height*2;
	for(GO invader:gos)if(invader.on)
	{count++;invader.incr(d);
		Bullet iBullet=((Invader)invader).bullet;
		if(iBullet.on)
		{iBullet.incr(d);
		 t=QDv2.player.dist(iBullet);
		 if(t<dist){dist=t;near=iBullet;}
		 if( QDv2.player.checkCollide( iBullet))
		 {lives--;QDv2.player.setOn(true);}
		 else if(t<8){lives--;QDv2.player.setOn(true);iBullet.setOn(false);}
		 else
		 {GO b=iBullet.isCollide(QDv2.bullets);
		  if(b!=null)
			iBullet.checkCollide(b);
		 }
		}
	}
   if(count==0)init();
  }
 }//class InvaderGOs
////////////////////////////////////////////////////////////////

	static Dimension _dim;
	static Dimension getDim(){return _dim;}
	static void setDim(Dimension p)
	{System.out.println("setDim:"+p);
		_dim=p;
		player.y=p.height-player.dim().height;}

	@Override	public void componentResized(ComponentEvent e)
	{setDim(this.getSize());}

	@Override	public void mouseMoved(MouseEvent e){player.x=e.getX();}
	@Override	public void mouseDragged(MouseEvent e) {player.x=e.getX();}
	@Override	public void mouseReleased(MouseEvent e){bullets.shootNew();}

	@Override	public void keyReleased(KeyEvent e)
	{//if fire button pressed
		bullets.shootNew();}

	@Override	public void keyTyped(KeyEvent e) {}
	@Override	public void keyPressed(KeyEvent e) {}
	@Override	public void componentMoved(ComponentEvent e) {}
	@Override	public void componentShown(ComponentEvent e) {}
	@Override	public void componentHidden(ComponentEvent e) {}
	@Override	public void mouseClicked(MouseEvent e) {}
	@Override	public void mousePressed(MouseEvent e) {}
	@Override	public void mouseEntered(MouseEvent e) {}
	@Override	public void mouseExited(MouseEvent e) {}

	/** entry point for main program.	 */
	public static void main(String[] args) {new QDv2();}

}//QDv2